package org.geekbang.projects.rest.core;

import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.InvocationCallback;
import javax.ws.rs.core.*;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

/**
* 抽象的请求处理  
* @author : lsq
*/
public abstract class AbstractHttpInvocation<T> extends URLHttpConnectionSupport implements Invocation {

	public final static String REQUEST_HEADER_COOKIE = "Cookie";

	public final static String REQUEST_HEADER_ACCEPT_LANGUAGE = "Accept-Language";

	public final static String REQUEST_HEADER_ACCEPT_ENCODING = "Accept-Encoding";

	public final static String REQUEST_HEADER = "Accept";

	private URL url;


	public AbstractHttpInvocation(HttpRequest request){
		super();
		initRequest(request);
	}

	/**
	 * 初始化请求
	 * @param request
	 */
	private void initRequest(HttpRequest request){
		this.url = request.getUrl();
		//初始化cookies
		Set<Cookie> cookies = request.getCookies();
		this.put(REQUEST_HEADER_COOKIE , cookies.stream().map(Cookie::toString));
		//初始化头
		MultivaluedMap<String, Object> headers = request.getHeaders();
		headers.forEach((key , value) -> {
			this.put(key , value.stream().map(Object::toString).collect(Collectors.joining(",")));
		});
		//初始化地区
		Set<Locale> locales = request.getLocales();
		this.put(REQUEST_HEADER_ACCEPT_LANGUAGE , locales.stream().map(Locale::toLanguageTag).collect(Collectors.joining(",")));
		//初始化好编码
		Set<String> encodings = request.getEncodings();
		this.put(REQUEST_HEADER_ACCEPT_ENCODING , encodings.stream().collect(Collectors.joining(",")));
		//初始化类型
		Set<MediaType> mediaTypes = request.getMediaTypes();
		this.put(REQUEST_HEADER , mediaTypes.stream().map(MediaType::toString).collect(Collectors.joining(",")));
	}

	@Override
	public Invocation property(String name, Object value) {
		assert name != null : "name不能为空";
		assert value != null : "value不能为空";
		this.put(name , value);
		return this;
	}

	@Override
	public Response invoke() {
		HttpURLConnection connection = (HttpURLConnection)getConnection(this.url);
		setMethod(connection , getMethod());
		postProcessor(connection);
		return createResponse(DefaultResponse.class , connection);
	}


	@Override
	public <T> T invoke(Class<T> responseType) {
		return invoke().readEntity(responseType);
	}

	@Override
	public <T> T invoke(GenericType<T> responseType) {
		return invoke().readEntity(responseType);
	}

	@Override
	public Future<Response> submit() {
		return executor.submit(() -> invoke());
	}

	@Override
	public <T> Future<T> submit(Class<T> responseType) {
		return executor.submit(() -> invoke(responseType));
	}

	@Override
	public <T> Future<T> submit(GenericType<T> responseType) {
		return executor.submit(() -> invoke(responseType));
	}

	@Override
	public <T> Future<T> submit(InvocationCallback<T> callback) {
		return (Future<T>) executor.submit(() -> {
			GenericType<T> entityType = null;
			Type[] genericInterfaces = callback.getClass().getGenericInterfaces();
			for(Type type : genericInterfaces){
				if (type instanceof ParameterizedType){
					Type rawType = ((ParameterizedType) type).getRawType();
					if (((Class)rawType).isAssignableFrom(InvocationCallback.class)){
						Type actualTypeArgument = ((ParameterizedType) type).getActualTypeArguments()[0];
						entityType = new GenericType<>(actualTypeArgument);
					}
				}
			}
			callback.completed(invoke().readEntity(entityType));
		});
	}

	/**
	 * 后置处理
	 * @param connection
	 */
	protected  void postProcessor(HttpURLConnection connection){
		/// 默认不做实现
	}

	/**
	 * 获取请求 method
	 * @return
	 */
	protected abstract String getMethod();
}
